package org.common.utils.tool;

import cn.hutool.core.util.ClassUtil;
import org.common.utils.field.MethodTable;
import org.common.utils.tableconfig.TableConfigManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * 类工具
 * @author zhouzhibing
 * @date 2025/5/19
 */
public class ClassTool {

    private static final Logger log = LoggerFactory.getLogger(ClassTool.class);

    /**
     * 空参数
     */
    private static final Class<?> [] EMPTY_PARAMETERS = new Class[0];

    /**
     * 获取包下所有类
     * @param packageName 包名
     * @return 类列表
     */
    public static Set<Class<?>> getAllClass(String packageName) {
        return ClassUtil.scanPackage(packageName);
    }

    /**
     * 获取指定包下所有子类
     * @param packageName 包名
     * @param clazz 父类
     * @return 子类列表
     */
    public static Set<Class<?>> getAllSubClass(String packageName , Class<?> clazz) {
        return ClassUtil.scanPackageBySuper(packageName, clazz);
    }

    /**
     * 获取指定类及其所有父类的所有字段列表
     * @param clazz 需要获取字段的类
     * @return 字段列表
     */
    public static List<Field> getAllFields(Class<?> clazz) {
        List<Field> fieldList = new ArrayList<>();
        Class<?> currentClass = clazz;

        // 循环遍历当前类及其父类，直到 Object 类
        while (currentClass != null) {
            Field[] fields = currentClass.getDeclaredFields();

            // 将当前类的字段添加到列表中
            for (Field field : fields) {
                fieldList.add(field);
            }

            // 获取父类以便下一次循环
            currentClass = currentClass.getSuperclass();
        }
        return fieldList;
    }

    /**
     * 获取指定类、方法名称和参数类型的方法
     * @param clazz  类
     * @param methodName 方法名称
     * @param parameterTypes 参数类型
     * @return  方法
     */
    public static Method getMethod(Class<?> clazz, String methodName , Class<?>... parameterTypes) {
        try {
            return clazz.getDeclaredMethod(methodName, parameterTypes);
        } catch (NoSuchMethodException e) {
            return null;
        }
    }

    /**
     * 获取指定类所有setter和getter方法
     * @param clzss  类
     * @return 方法列表
     */
    public static MethodTable getSetGetMethodList(Class<?> clzss) {
        return getSetGetMethodList(clzss , null);
    }

    /**
     * 获取指定类所有setter和getter方法
     * @param clzss  类
     * @param filter 过滤器
     * @return 方法列表
     */
    public static MethodTable getSetGetMethodList(Class<?> clzss , Filter filter) {
        Field[] fields = clzss.getDeclaredFields();
        if(CollectionTool.isEmpty(fields))
            return MethodTable.getDefault();

        int length = fields.length;
        List<Method> setMethodList = new ArrayList<>(length);
        List<Method> getMethodList = new ArrayList<>(length);

        for (Field field : fields) {
            String methodName = StringTool.toMaxHumpString(field.getName());
            String setMethodName = "set" + methodName;
            String getMethodName = "get" + methodName;
            Method setMethod = getMethod(clzss, setMethodName, field.getType());
            Method getMethod = getMethod(clzss, getMethodName, EMPTY_PARAMETERS);

            if(filter != null && !filter.filter(clzss, field, setMethod, getMethod))
                continue;

            if(setMethod != null) {
                setMethod.setAccessible(true);
                setMethodList.add(setMethod);
            }
            if(getMethod != null) {
                getMethod.setAccessible(true);
                getMethodList.add(getMethod);
            }
        }
        return new MethodTable(setMethodList , getMethodList);
    }


    /**
     * 获取指定包下类型实体列表
     * @param packageName 包地址
     * @return 实体列表 <实体类型，实体实例>
     */
    public Map<Class<?>, Object> getInstanceMap(String packageName , Consumer<Object> consumer) {
        Set<Class<?>> classSet = ClassUtil.scanPackage(packageName);
        Map<Class<?>, Object> instanceMap = new HashMap<>(classSet.size());
        for (Class<?> clzss : classSet) {
            try {
                Object instance = clzss.getDeclaredConstructor().newInstance();
                if(consumer != null)
                    consumer.accept(instance);
                instanceMap.put(clzss, instance);
            } catch (Exception e) {
                log.error("ClassTool.getInstance error , packageName = {} , clzss = {} " , packageName , clzss , e);
            }
        }
        return instanceMap;
    }


    public interface Filter {

        boolean filter(Class<?> clzss , Field field , Method setMethod , Method getMethod);
    }
}
